{\rtf1\ansi\ansicpg950\deff0\deflang1033\deflangfe1028{\fonttbl{\f0\fmodern\fprq6\fcharset136 \'b7\'73\'b2\'d3\'a9\'fa\'c5\'e9;}}
\viewkind4\uc1\pard\lang1028\f0\fs32 My assignment 5\par
\par
=\'a6\'b3\'ad\'fe\'a8\'c7\'b7\'73\'aa\'ba\'a7\'ef\'c5\'dc=\par
*Undo\'bd\'d0\'bd\'d0\'aa\'bd\'b1\'b5\'b0\'d1\'be\'5c http://ccl.cs.nctu.edu.tw/class.wiki/index.php/Cbh-Assignment-5#.E8.A8.AD.E8.A8.88:_.E8.A3.BD.E4.BD.9CUndo.E5.8A.9F.E8.83.BD\par
*XML IO\'b3\'a1\'a4\'c0\'bd\'d0\'aa\'bd\'b1\'b5\'b0\'d1\'be\'5c http://ccl.cs.nctu.edu.tw/class.wiki/index.php/Cbh-Assignment-5#.E8.A8.AD.E8.A8.88:_.E8.A3.BD.E4.BD.9CXML_I.2FO.E5.8A.9F.E8.83.BD\par
*\'a7\'ef\'c5\'dc\'a5\'bb\'a4\'e5\'a5\'f3\'aa\'ba\'ae\'e6\'a6\'a1\'b5\'b2\'ba\'63, \'a5\'48\'ab\'4b\'bc\'75\'a9\'ca\'af\'c7\'a4\'4a\'b3\'76\'a8\'42\'bc\'57\'b2\'4b\'aa\'ba\'b3\'5d\'ad\'70\'b9\'4c\'b5\'7b\par
*\'b1\'4e\'b9\'4c\'a5\'68\'a9\'d2\'a6\'b3\'aa\'ba\'a7\'40\'b7\'7e\'a4\'ba\'ae\'65\'b3\'a3\'be\'e3\'a6\'58\'b6\'69\'a5\'bb\'a4\'e5\'a5\'f3\par
*\'b7\'73\'bc\'57Undo\'a5\'5c\'af\'e0, \'bb\'50XML IO\'a5\'5c\'af\'e0\par
*Undo\'a5\'48\'a4\'ceXML IO\'b3\'a3\'a6\'b3\'a6\'68\'ba\'d8\'b3\'5d\'ad\'70\'a4\'e2\'aa\'6b\'a5\'69\'a5\'48\'a7\'e0\'be\'dc, \'a8\'c3\'a5\'42\'a6\'43\'a5\'58\'c0\'75\'af\'ca\'c2\'49\par
*\'b7\'73\'bc\'57\'a5\'5bUnit Test\'a5\'48\'a4\'ceTesting Script\'aa\'ba\'b4\'79\'ad\'7a\par
\par
=\'b1\'4d\'ae\'d7\'b7\'a7\'ad\'7a=\par
Book\'b1\'4d\'ae\'d7, \'b9\'ea\'a7\'40\'a4\'40\'a5\'bb\'ae\'d1\'c4\'79\'aa\'ba\'b3\'b9\'b8\'60\'b7\'73\'bc\'57/\'a7\'52\'b0\'a3/\'c5\'e3\'a5\'dc\'b5\'a5\'a5\'5c\'af\'e0, \'a5\'48Book Model\'aa\'baComposite Pattern\'ac\'b0\'ae\'d6\'a4\'df, \'b3\'76\'a8\'42\'b1\'c4\'a5\'ceMVC\'ac\'5b\'ba\'63\'bc\'57\'b2\'4b\'b7\'73\'a5\'5c\'af\'e0.\par
  \par
=\'b3\'5d\'ad\'70: Book Model\'a5\'4e\'aa\'ed\'ae\'d1\'a5\'bb\'bb\'50\'b3\'b9\'b8\'60\'a4\'ba\'ae\'65=\par
*\'a8\'cf\'a5\'cePython\'bb\'79\'a8\'a5\'b9\'ea\'a7\'40 class Book, \'a8\'c3\'a5\'42\'a5\'69\'a5\'48print\'a6\'4c\'a5\'58\'a4\'ba\'ae\'65\par
*\'c5\'fdBook Model\'bb\'bc\'b0\'6a\'a6\'4c\'a5\'58\'a5\'fe\'b3\'a1\'a4\'ba\'ae\'65\par
*\'a7\'da\'aa\'ba\'b3\'5d\'ad\'70\'b1\'4e ChapterSection \'b7\'ed\'a6\'a8\'ac\'4f\'b1\'5f\'aa\'ac\'c3\'f6\'ab\'59, \'a5\'69\'a5\'48\'aa\'ed\'a5\'dc\'a5\'58\'a5\'48\'a4\'55\'aa\'ba\'b3\'b9\'b8\'60\'be\'f0\'aa\'ac\'b5\'b2\'ba\'63\par
  Chapter 1\par
    1.1\par
      1.1.1\par
      1.1.2\par
    2.1\par
      2.1.1\par
      ...\par
\par
\'a6\'b9\'b9\'cf\'ac\'b0\'c2\'b2\'a9\'f6\'aa\'baclass diagram\par
  [[Image:cbhAssignment2a.jpg]]\par
\par
\'a6\'b9\'b9\'cf\'ac\'b0\'c2\'b2\'a9\'f6\'aa\'ba\'a6\'43\'a6\'4c\'a5\'58\'b3\'b9\'b8\'60\'aa\'basequence diagram\par
  [[Image:cbhAssignment2b.jpg]]\par
\par
=\'b3\'5d\'ad\'70: \'a8\'cf\'a5\'ceComposite Pattern\'a7\'ef\'bc\'67Book Model=\par
\'a8\'cf\'a5\'ceComposite Pattern\'b9\'ea\'a7\'40 class Book, \'a6\'b3\'a5\'48\'a4\'55\'c3\'fe\'a7\'4f\par
#Book (\'a5\'4e\'aa\'ed\'a4\'40\'a5\'bb\'ae\'d1)\par
#Composite (\'a5\'4e\'aa\'ed Composite Pattern \'b7\'ed\'a4\'a4\'aa\'ba Composite Class)\par
#BookComponent (\'a5\'4e\'aa\'ed Composite Class \'a4\'a4\'aa\'ba\'b0\'f2\'c2\'a6\'a4\'b8\'af\'c0)\par
#Section (\'b3\'b9\'b8\'60)\par
#Paragraph (\'ac\'71\'b8\'a8\'a4\'e5\'a6\'72)\par
#UnorderedList (\'a8\'53\'a6\'b3\'b6\'b6\'a7\'c7\'aa\'baBulletItem\'b6\'b0\'a6\'58)\par
#BulletItem (\'a4\'ba\'a7\'74\'a6\'62UnorderedList\'b7\'ed\'a4\'a4\'aa\'baBullet Item)\par
\par
\'b0\'f2\'a5\'bb\'aa\'ba Composite Pattern \'b3\'5d\'ad\'70\'a6\'70\'a4\'55\par
  [[Image:cbhAssignment3a.jpg]]\par
\par
\'a7\'da\'aa\'ba\'b3\'5d\'ad\'70\'b1\'4e ChapterSection \'b7\'ed\'a6\'a8\'ac\'4f\'b1\'5f\'aa\'ac\'c3\'f6\'ab\'59,\'a5\'69\'a5\'48\'aa\'ed\'a5\'dc\'a5\'58\'a5\'48\'a4\'55\'aa\'ba\'b3\'b9\'b8\'60\'be\'f0\'aa\'ac\'b5\'b2\'ba\'63,\'a8\'c3\'bc\'57\'a5\'5bUnorderedList\'bb\'50BulletItem,\'a5\'69\'a5\'48\'b4\'4f\'a4\'4a\'b4\'79\'ad\'7a\'b1\'f8\'a6\'43\'aa\'ba\'a4\'e5\'a6\'72\par
\par
  Chapter 1\par
    1.1\par
      1.1.1\par
        . bulletItem1\par
        . bulletItem2\par
        . bulletItem3\par
      1.1.2\par
    2.1\par
      2.1.1\par
      ...\par
\par
\'a6\'b9\'b9\'cf\'ac\'b0\'c2\'b2\'a9\'f6\'aa\'baclass diagram\par
  [[Image:cbhAssignment3b.jpg]]\par
\par
=\'b3\'5d\'ad\'70: \'a8\'cf\'a5\'ceMVC\'ac\'5b\'ba\'63\'a9\'ee\'a4\'c0\'b3\'64\'a5\'f4=\par
* \'a9\'ee\'a4\'c0\'ac\'b0MVC\'ac\'5b\'ba\'63\'b1\'4e\'c5\'e3\'a5\'dc\'aa\'ba\'b3\'64\'a5\'f4\'a5\'d1Model\'c2\'e0\'b2\'be\'a8\'ecView\'a9\'fa\'bd\'54\'a4\'c0\'a4\'75\par
* \'a8\'cf\'a5\'ceMVC\'aa\'ba\'a4\'e8\'a6\'a1\'a7\'ef\'bc\'67Book project\par
* Refactoring, \'a7\'e2\'a9\'d2\'a6\'b3\'aa\'baprint\'b3\'a3\'c2\'e0\'b2\'be\'a8\'ecview.py package\par
* \'c5\'fdview.py\'b1\'4d\'aa\'f9\'ad\'74\'b3\'64User Interface\par
* \'a4\'b9\'b3\'5c\'a5\'ce\'a4\'e1\'b9\'ef\'be\'e3\'ad\'d3 model.py \'b6\'69\'a6\'e6: \'b7\'73\'bc\'57, \'a7\'52\'b0\'a3, \'c5\'e3\'a5\'dc, \'ab\'d8\'a5\'df\'b7\'73\'a4\'b8\'af\'c0 \'b5\'a5\'be\'de\'a7\'40\par
* \'a9\'d2\'a6\'b3\'aa\'baUI\'b3\'a3\'b1\'c4\'a5\'ce console \'a4\'e8\'a6\'a1\'b1\'b5\'a6\'ac\'a5\'ce\'a4\'e1\'bf\'e9\'a4\'4a\par
* \'b7\'edview.py\'b1\'b5\'a6\'ac\'a8\'ec\'a5\'ce\'a4\'e1\'bf\'e9\'a4\'4a\'a4\'a7\'ab\'e1, \'b7\'7c\'a9\'49\'a5\'73 controller.py \'b3\'6f\'ad\'d3package\'b6\'69\'a6\'e6\'a6\'55\'ba\'d8\'be\'de\'a7\'40\par
\par
== package model.py ==\par
#Book (\'a5\'4e\'aa\'ed\'a4\'40\'a5\'bb\'ae\'d1)\par
#Composite (\'a5\'4e\'aa\'ed Composite Pattern \'b7\'ed\'a4\'a4\'aa\'ba Composite Class)\par
#BookComponent (\'a5\'4e\'aa\'ed Composite Class \'a4\'a4\'aa\'ba\'b0\'f2\'c2\'a6\'a4\'b8\'af\'c0)\par
#Section (\'b3\'b9\'b8\'60)\par
#Paragraph (\'ac\'71\'b8\'a8\'a4\'e5\'a6\'72)\par
#UnorderedList (\'a8\'53\'a6\'b3\'b6\'b6\'a7\'c7\'aa\'baBulletItem\'b6\'b0\'a6\'58)\par
#BulletItem (\'a4\'ba\'a7\'74\'a6\'62UnorderedList\'b7\'ed\'a4\'a4\'aa\'baBullet Item)\par
\par
== package view.py ==\par
#ViewBook\par
\par
== package controller.py ==\par
*ControllerBook\par
*#\'a4\'b9\'b3\'5ccommand line\'bf\'e9\'a4\'4a\'ab\'fc\'a5\'4f\'be\'de\'a7\'40\'be\'e3\'a5\'bb\'ae\'d1\par
*#new\'ab\'fc\'a5\'4f: \'a5\'69\'a5\'48\'ab\'d8\'a5\'df\'a9\'d2\'a6\'b3Model\'a4\'a4\'aa\'baClass\'aa\'ab\'a5\'f3, Section, Paragraph, UnorderedList, BulletItem\par
*#add\'ab\'fc\'a5\'4f: \'a5\'69\'a5\'48\'a7\'e2 child \'a4\'b8\'af\'c0\'a5\'5b\'a4\'4a parent \'a4\'b8\'af\'c0, \'bc\'68\'bc\'68\'ae\'4d\'c5\'7c Model \'aa\'ba\'aa\'ab\'a5\'f3\par
*#delete\'ab\'fc\'a5\'4f: \'a5\'69\'a5\'48\'ae\'da\'be\'dastrTitle\'bb\'bc\'b0\'6a\'a7\'52\'b0\'a3\'a9\'d2\'a6\'b3\'b8\'d3 title \'a5\'48\'a4\'55\'aa\'ba\'a9\'d2\'a6\'b3\'b8\'ea\'ae\'c6\par
*#show\'ab\'fc\'a5\'4f: \'a5\'69\'a5\'48\'c5\'e3\'a5\'dc\'be\'de\'a7\'40\'b9\'4c\'ab\'e1\'aa\'ba\'b3\'cc\'b7\'73\'aa\'ac\'ba\'41, \'c5\'e3\'a5\'dc\'be\'e3\'a5\'bb\'ae\'d1\par
\par
== UML\'b9\'cf\'a7\'ce\'bb\'50\'bb\'a1\'a9\'fa ==\par
*\'b1\'4e package \'b5\'b2\'ba\'63\'a9\'ee\'a4\'c0\'ac\'b0MVC\'aa\'ba\'a4\'e8\'a6\'a1\'b2\'d5\'a6\'a8\par
*\'a4\'54\'ad\'d3 package \'a4\'c0\'a7\'4f\'ac\'4f model.py, view.py, controller.py\par
*model.py \'aa\'ba\'b3\'a1\'a4\'c0, \'b1\'c4\'a5\'ce composite pattern \'a7\'b9\'a6\'a8\par
*\'bc\'57\'a5\'5bUnorderedList\'bb\'50BulletItem,\'a5\'69\'a5\'48\'b4\'4f\'a4\'4a\'b4\'79\'ad\'7a\'b1\'f8\'a6\'43\'aa\'ba\'a4\'e5\'a6\'72\par
*Section \'b7\'ed\'a6\'a8\'ac\'4f\'b1\'5f\'aa\'ac\'c3\'f6\'ab\'59,\'a5\'69\'a5\'48\'aa\'ed\'a5\'dc\'a5\'58\'b3\'b9\'b8\'60\'be\'f0\'aa\'ac\'b5\'b2\'ba\'63\par
\par
    Chapter 1\par
      1.1\par
         1.1.1\par
          . bulletItem1\par
          . bulletItem2\par
          . bulletItem3\par
        1.1.2\par
      2.1\par
        2.1.1\par
        ...\par
\par
*\'a4\'55\'b9\'cfclass diagram, \'a4\'57\'a5\'62\'b3\'a1\'ac\'b0package diagram\par
*\'a4\'55\'a5\'62\'b3\'a1\'ac\'4fclass, \'ab\'f6\'b7\'d3package\'aa\'ba\'b0\'cf\'a4\'c0\'aa\'6b\par
*\'a4\'57\'a4\'e8 controller.py, \'a4\'de\'a5\'ce\'a8\'ec model.py, view.py\par
*\'a5\'aa\'a4\'55\'a8\'a4\'ac\'4f view.py, \'a4\'de\'a5\'ce\'a8\'ec model.py\par
*\'a5\'6b\'a4\'55\'a8\'a4\'ac\'4f model.py, \'a7\'b9\'a5\'fe\'bf\'57\'a5\'df, \'a8\'53\'a6\'b3\'a4\'de\'a5\'ce\'a8\'e4\'a5\'4c package\par
    [[Image:cbhAssignment4a.jpg]]\par
\par
=\'b3\'5d\'ad\'70: \'bb\'73\'a7\'40Undo\'a5\'5c\'af\'e0=\par
==\'b3\'5d\'ad\'70\'a5\'d8\'bc\'d0\'bb\'50\'b0\'dd\'c3\'44\'b4\'79\'ad\'7a==\par
\'a6\'62Book\'b1\'4d\'ae\'d7\'b7\'ed\'a4\'a4, \'b7\'73\'bc\'57\'a9\'ce\'a7\'52\'b0\'a3Chapter,Section\'a9\'ceParagraph\'b5\'a5\'a4\'b8\'af\'c0, \'a5\'69\'a5\'48\'a7\'ef\'c5\'dc\'ae\'d1\'a4\'a4\'aa\'ba\'b3\'a1\'a4\'c0\'a4\'ba\'ae\'65, \'b5\'4d\'a6\'d3\'a8\'cf\'a5\'ce\'aa\'cc\'a9\'ce\'b3\'5c\'a6\'62\'bc\'57\'a7\'52\'a4\'ba\'ae\'65\'aa\'ba\'ae\'c9\'ad\'d4, \'b7\'7c\'a6\'b3\'bb\'7e\'be\'de\'a7\'40\'a9\'ce\'aa\'cc\'a4\'cf\'ae\'ac\'aa\'ba\'b1\'a1\'a7\'ce\'b5\'6f\'a5\'cd, \'a6\'b9\'ae\'c9\'b4\'a3\'a8\'d1Undo\'a5\'5c\'af\'e0\'b4\'4e\'c5\'e3\'b1\'6f\'a6\'b3\'a8\'e4\'a5\'b2\'ad\'6e, \'a5\'bb\'b3\'e6\'a4\'b8\'aa\'ba\'bb\'dd\'a8\'44\'b4\'4e\'ac\'4f\'b7\'73\'bc\'57Undo\'a9\'52\'a5\'4f, \'c5\'fd\'a8\'cf\'a5\'ce\'aa\'cc\'a5\'69\'a5\'48\'a6\'62\'a6\'b3\'bb\'dd\'ad\'6e\'aa\'ba\'ae\'c9\'ad\'d4, \'a7\'51\'a5\'ceUndo\'a5\'5c\'af\'e0\'a6\'56\'ab\'65\'a6\'5e\'b4\'5f\'a8\'ec\'a4\'57\'a4\'40\'ad\'d3\'a8\'42\'c6\'4a\'aa\'ba\'aa\'ac\'ba\'41.\par
\par
==\'b3\'e6\'a4\'b8\'b4\'fa\'b8\'d5(Unit Test)==\par
\'b7\'ed\'a8\'cf\'a5\'ce\'aa\'cc\'a6\'62Console\'bf\'e9\'a4\'4aundo\'a9\'52\'a5\'4f\'ae\'c9, MVC\'ac\'5b\'ba\'63\'b7\'ed\'a4\'a4\'aa\'baController\'b7\'7c\'ad\'74\'b3\'64\'b1\'b5\'a6\'ac\'a8\'c3\'b0\'f5\'a6\'e6\'a6\'b9\'a9\'52\'a5\'4f, Unit Test\'aa\'ba\'b3\'5d\'ad\'70\'a4\'c0\'a6\'a8Undo\'b7\'73\'bc\'57, \'a5\'48\'a4\'ceUndo\'a7\'52\'b0\'a3\'a8\'e2\'ad\'d3Test Case, \'c1\'f6\'b5\'4d\'a8\'53\'a6\'b3\'b2\'5b\'bb\'5c\'a9\'d2\'a6\'b3\'aa\'ba\'b4\'fa\'b8\'d5\'b6\'b5\'a5\'d8, \'a6\'fd\'ac\'4f\'a4\'77\'b8\'67\'a5\'5d\'a7\'74\'a4\'46\'b3\'cc\'ad\'ab\'ad\'6e\'aa\'ba\'a8\'e2\'ba\'d8, \'ac\'b0\'a4\'46\'b8\'60\'ac\'d9Unit Test Coding\'a6\'a8\'a5\'bb, \'a7\'b9\'be\'e3\'aa\'ba\'b4\'fa\'b8\'d5\'b1\'4e\'ab\'4f\'af\'64\'b5\'b9QA Testing Script\'b6\'69\'a6\'e6, \'a5\'48\'a4\'55\'ac\'b0\'a8\'e2\'ba\'d8Test Case\'aa\'ba\'bb\'a1\'a9\'fa:\par
\par
*testAddThenUndo() : \'b4\'fa\'b8\'d5\'a5\'fd\'b7\'73\'bc\'57Paragraph\'ab\'e1Undo\'ac\'4f\'a7\'5f\'a6\'a8\'a5\'5c\par
#\'bc\'57\'a5\'5b\'a4\'40\'ad\'d3Paragraph\'a8\'ecModel\'b7\'ed\'a4\'a4\par
#\'bd\'54\'bb\'7bModel\'b7\'ed\'a4\'a4\'a4\'77\'b8\'67\'a5\'5d\'a7\'74\'b7\'73\'bc\'57\'aa\'baParagraph\par
#\'a9\'49\'a5\'73undo\'a9\'52\'a5\'4f\par
#\'c0\'cb\'ac\'64Model\'ac\'4f\'a7\'5f\'a4\'77\'b8\'67\'a6\'a8\'a5\'5c\'b2\'be\'b0\'a3\'b8\'d3Paragraph\par
\par
*testDeleteThenUndo : \'b4\'fa\'b8\'d5\'a5\'fd\'a7\'52\'b0\'a3Paragraph\'ab\'e1Undo\'ac\'4f\'a7\'5f\'a6\'a8\'a5\'5c\par
#\'a5\'fd\'b1\'71\'b9\'77\'b3\'5d\'aa\'baModel\'b7\'ed\'a4\'a4\'b0\'f5\'a6\'e6delete\'a9\'52\'a5\'4f\'a7\'52\'b0\'a3\'ac\'59\'ad\'d3Paragraph\par
#\'bd\'54\'bb\'7bModel\'b7\'ed\'a4\'a4\'a4\'77\'b8\'67\'a4\'a3\'a6\'73\'a6\'62\'b8\'d3Paragraph\par
#\'a9\'49\'a5\'73undo\'a9\'52\'a5\'4f\par
#\'c0\'cb\'ac\'64\'ac\'4f\'a7\'5f\'b8\'d3Paragraph\'a6\'a8\'a5\'5c\'aa\'ba\'b3\'51\'a5\'5b\'a6\'5e\'a5\'68Model\'b7\'ed\'a4\'a4\par
\par
==QA\'b4\'fa\'b8\'d5\'b8\'7d\'a5\'bb(QA Testing Script)==\par
QA\'b4\'fa\'b8\'d5\'aa\'ba\'b3\'a1\'a4\'c0\'b1\'4e\'a5\'5d\'a7\'74\'a5\'48\'a4\'55\'b4\'fa\'b8\'d5\'b6\'b5\'a5\'d8, \'a5\'d1QA\'a4\'48\'ad\'fb\'a8\'cf\'a5\'ceConsole\'b9\'ea\'bb\'da\'b6\'69\'a6\'e6\'a4\'e2\'b0\'ca\'be\'de\'a7\'40, \'b4\'fa\'b8\'d5\'a8\'c3\'b0\'4f\'bf\'fd\'b5\'b2\'aa\'47\'ac\'4f\'a7\'5f\'a5\'bf\'bd\'54.\par
#\'b7\'73\'bc\'57add\'a4\'54\'ad\'d3Chapter\'a8\'c3\'a5\'42undo\'a8\'e2\'a6\'b8, \'ac\'4f\'a7\'5f\'a6\'a8\'a5\'5c?\par
#\'b7\'73\'bc\'57add\'a4\'54\'ad\'d3Section\'a8\'c3\'a5\'42undo\'a8\'e2\'a6\'b8, \'ac\'4f\'a7\'5f\'a6\'a8\'a5\'5c?\par
#\'b7\'73\'bc\'57add\'a4\'54\'ad\'d3Paragraph\'a8\'c3\'a5\'42undo\'a8\'e2\'a6\'b8, \'ac\'4f\'a7\'5f\'a6\'a8\'a5\'5c?\par
#\'b7\'73\'bc\'57add\'a4\'54\'ad\'d3BulletItem\'a8\'c3\'a5\'42undo\'a8\'e2\'a6\'b8, \'ac\'4f\'a7\'5f\'a6\'a8\'a5\'5c?\par
#\'a7\'52\'b0\'a3delete\'a4\'54\'ad\'d3Chapter\'a8\'c3\'a5\'42undo\'a8\'e2\'a6\'b8, \'ac\'4f\'a7\'5f\'a6\'a8\'a5\'5c?\par
#\'a7\'52\'b0\'a3delete\'a4\'54\'ad\'d3Section\'a8\'c3\'a5\'42undo\'a8\'e2\'a6\'b8, \'ac\'4f\'a7\'5f\'a6\'a8\'a5\'5c?\par
#\'a7\'52\'b0\'a3delete\'a4\'54\'ad\'d3Paragraph\'a8\'c3\'a5\'42undo\'a8\'e2\'a6\'b8, \'ac\'4f\'a7\'5f\'a6\'a8\'a5\'5c?\par
#\'a7\'52\'b0\'a3delete\'a4\'54\'ad\'d3BulletItem\'a8\'c3\'a5\'42undo\'a8\'e2\'a6\'b8, \'ac\'4f\'a7\'5f\'a6\'a8\'a5\'5c?\par
\par
==\'b3\'5d\'ad\'70\'a4\'e8\'aa\'6b: \'b9\'42\'a5\'ceCommand Pattern\'bb\'73\'a7\'40Undo==\par
Undo\'a5\'5c\'af\'e0\'aa\'ba\'bb\'73\'a7\'40\'a6\'62Design Pattern\'aa\'ba\'ba\'44\'a8\'d2\'a4\'57\'ac\'4f\'b1\'c4\'a5\'ceCommand Pattern\'a8\'d3\'a7\'b9\'a6\'a8, \'a6\'62Book\'b1\'4d\'ae\'d7\'b7\'ed\'a4\'a4, \'b4\'4e\'ac\'4f\'b1\'4e\'b7\'73\'bc\'57\'bb\'50\'a7\'52\'b0\'a3\'aa\'ba\'a9\'52\'a5\'4f\'a5\'5d\'b8\'cb\'a6\'62Command\'c3\'fe\'a7\'4f\'b7\'ed\'a4\'a4, \'a8\'c3\'a6\'62\'a6\'55Command\'c3\'fe\'a7\'4f\'b7\'ed\'a4\'a4\'b4\'a3\'a8\'d1undo\'a4\'e8\'aa\'6b, \'af\'e0\'b0\'f7\'a7\'e2Model\'ab\'ec\'b4\'5f\'a8\'ec\'ab\'65\'ad\'b1\'b4\'58\'ad\'d3\'a8\'42\'c6\'4a\'aa\'ba\'aa\'ac\'ba\'41.\par
\par
==\'a7\'e0\'be\'dc\'b3\'5d\'ad\'70\'a4\'e2\'aa\'6b==\par
\'c1\'f6\'b5\'4d\'a4\'77\'b8\'67\'bd\'54\'a9\'77Undo\'a5\'5c\'af\'e0\'ac\'4f\'b1\'c4\'a5\'ceCommand Pattern\'a8\'d3\'bb\'73\'a7\'40, \'a6\'62\'b3\'5d\'ad\'70\'aa\'ba\'bf\'ef\'be\'dc\'a4\'57, Undo\'a4\'b4\'b5\'4d\'a6\'b3\'b4\'58\'ad\'d3\'b9\'ea\'a7\'40\'a4\'e2\'aa\'6b\'a5\'b2\'b6\'b7\'a7\'e0\'be\'dc: \par
*Transaction Commit\'a4\'e2\'aa\'6b:\par
**\'bb\'a1\'a9\'fa: \'a6\'62\'b8\'ea\'ae\'c6\'ae\'77\'aa\'ba\'bb\'e2\'b0\'ec, \'b8\'ea\'ae\'c6\'aa\'ba\'a7\'f3\'b0\'ca\'a8\'c3\'a4\'a3\'ac\'4f\'aa\'bd\'b1\'b5\'a5\'c3\'a4\'5b\'bc\'67\'a4\'4a\'c0\'78\'a6\'73\'b8\'cb\'b8\'6d, \'a6\'d3\'ac\'4f\'a6\'62\'bc\'c6\'ad\'d3\'bc\'57\'a7\'52\'ad\'d7\'a7\'ef\'b5\'a5\'be\'de\'a7\'40\'a4\'a7\'ab\'e1, \'a5\'b2\'b6\'b7\'b0\'f5\'a6\'e6\'a4\'40\'ad\'d3Commit\'a9\'52\'a5\'4f, \'a4\'7e\'b7\'7c\'af\'75\'a5\'bf\'bc\'67\'a4\'4a. \'a6\'62Commit\'a4\'a7\'ab\'65\'aa\'ba\'a9\'d2\'a6\'b3\'be\'de\'a7\'40\'b3\'a3\'a5\'75\'ac\'4f\'b0\'4f\'bf\'fd\'a6\'62Runtime Memory, \'c0\'48\'ae\'c9\'a4\'b9\'b3\'5c\'a9\'f1\'b1\'f3\'a9\'ceUndo, \'b3\'6f\'bc\'cb\'aa\'ba\'b3\'5d\'ad\'70\'a5\'69\'a5\'48\'bc\'57\'a5\'5b\'b0\'f5\'a6\'e6\'ae\'c4\'b2\'76, \'a6\'5d\'ac\'b0\'a4\'6a\'b6\'71\'b8\'ea\'ae\'c6\'aa\'ba\'b5\'77\'ba\'d0I/O\'b3\'71\'b1\'60\'a4\'f1\'b0\'4f\'be\'d0\'c5\'e9\'be\'de\'a7\'40\'af\'d3\'ae\'c9\'ab\'dc\'a6\'68, \'b0\'a3\'a4\'46\'ae\'c4\'af\'e0\'a6\'d2\'bc\'7b\'a4\'a7\'a5\'7e, \'a4\'5d\'a5\'69\'a5\'48\'b4\'a3\'a8\'d1\'b1\'4e\'be\'e3\'ad\'d3Transaction Roll Back\'aa\'ba\'ae\'c4\'aa\'47.\par
\par
*\'ab\'4f\'a6\'73Model Snapshot\'be\'fa\'a5\'76\'ac\'f6\'bf\'fd\'a4\'e2\'aa\'6b:\par
**\'bb\'a1\'a9\'fa: \'a8\'43\'a6\'b8\'b7\'73\'bc\'57\'a7\'52\'b0\'a3\'aa\'ba\'a9\'52\'a5\'4f\'a4\'55\'b9\'46\'a4\'a7\'ab\'e1, \'b1\'4eModel\'b2\'a7\'b0\'ca\'aa\'ba\'b5\'b2\'aa\'47(Model Snapshot)\'c0\'78\'a6\'73\'b0\'5f\'a8\'d3, \'a6\'70\'aa\'47\'a6\'b3\'a5\'b2\'ad\'6e\'ab\'ec\'b4\'5f\'ab\'65\'ad\'b1\'b4\'58\'a6\'b8\'aa\'ba\'b5\'b2\'aa\'47\'ae\'c9, \'aa\'bd\'b1\'b5\'bd\'d5\'a5\'58Snapshot\'a8\'fa\'a5\'4e\'a5\'d8\'ab\'65\'a5\'bf\'a6\'62\'a8\'cf\'a5\'ce\'aa\'baModel\'a7\'59\'a5\'69.\par
**\'c0\'75\'c2\'49 : \'a6\'68\'a6\'b8Undo\'aa\'ba\'b0\'f5\'a6\'e6\'b3\'74\'ab\'d7\'ad\'b8\'a7\'d6, \'a6\'5d\'ac\'b0\'a5\'69\'aa\'bd\'b1\'b5\'bd\'d5\'a5\'58\'b7\'ed\'ae\'c9\'aa\'ba\'b5\'b2\'aa\'47\par
**\'af\'ca\'c2\'49 : \'a5\'69\'af\'e0\'b7\'7c\'af\'d3\'a5\'ce\'ab\'dc\'a6\'68\'b0\'4f\'be\'d0\'c5\'e9\par
**\'af\'ca\'c2\'49\'a7\'ef\'b6\'69 : Model Snapshot\'a5\'69\'a5\'48\'b1\'c4\'a5\'ce\'b6\'c8\'ac\'f6\'bf\'fdDiff\'ae\'74\'b2\'a7\'aa\'ba\'a4\'e8\'a6\'a1, \'a4\'6a\'b4\'54\'ab\'d7\'c0\'a3\'c1\'59\'b0\'4f\'be\'d0\'c5\'e9\'af\'d3\'a5\'ce\'b6\'71\par
\par
*\'b0\'f5\'a6\'e6\'a4\'cf\'ab\'fc\'a5\'4f\'a4\'e2\'aa\'6b:\par
**\'bb\'a1\'a9\'fa: \'b7\'73\'bc\'57\'aa\'ba\'ac\'db\'a4\'cf\'ab\'fc\'a5\'4f\'ac\'4f\'a7\'52\'b0\'a3, \'a6\'5d\'a6\'b9\'a6\'70\'aa\'47\'b7\'51\'ad\'6eUndo\'b7\'73\'bc\'57\'aa\'ba\'ae\'c4\'aa\'47, \'a5\'69\'a5\'48\'a5\'ce\'b0\'f5\'a6\'e6\'ac\'db\'b9\'ef\'c0\'b3\'aa\'ba\'a7\'52\'b0\'a3\'ab\'fc\'a5\'4f\'b9\'46\'a6\'a8\par
**\'c0\'75\'c2\'49:\par
**#\'ac\'d9\'b0\'4f\'be\'d0\'c5\'e9, \'a5\'75\'ad\'6e\'b0\'4f\'bf\'fd\'a4\'cf\'ab\'fc\'a5\'4f\'a6\'ea, \'b4\'4e\'a5\'69\'a5\'48\'b0\'b5\'a8\'ec\'a6\'68\'a6\'b8Undo\par
**#\'b0\'f5\'a6\'e6\'b3\'74\'ab\'d7\'a8\'ce\par
**\'af\'ca\'c2\'49: \'a6\'b3\'a8\'c7\'ab\'fc\'a5\'4f\'ab\'dc\'c3\'f8\'a7\'e4\'a8\'ec\'a4\'cf\'ab\'fc\'a5\'4f, \'a8\'d2\'a6\'70\'c3\'b8\'b9\'cf\'b3\'6e\'c5\'e9\'a6\'70\'aa\'47\'a6\'62\'b3\'42\'b2\'7a\'a4\'40\'b1\'69\'b7\'d3\'a4\'f9\'ae\'c9, \'b0\'b5\'a4\'46\'ac\'59\'ad\'d3\'c3\'fa\'a4\'c6\'af\'53\'ae\'c4\'a4\'a7\'ab\'e1, \'ad\'6e\'a7\'e4\'a8\'ec"\'a4\'cf\'c3\'fa\'a4\'c6\'ab\'fc\'a5\'4f" \'b4\'4e\'c5\'e3\'b1\'6f\'a4\'51\'a4\'c0\'a7\'78\'c3\'f8, \'a6\'fd\'a6\'70\'aa\'47\'ac\'4f\'b8\'ea\'ae\'c6\'ae\'77\'aa\'ba\'b7\'73\'bc\'57/\'a7\'52\'b0\'a3\'ac\'59\'b5\'a7\'b8\'ea\'ae\'c6\'b4\'4e\'ab\'dc\'ae\'65\'a9\'f6\'a9\'bc\'a6\'b9\'a4\'ac\'ac\'b0\'a4\'cf\'ab\'fc\'a5\'4f.\par
\par
*\'ad\'ab\'b7\'73\'ab\'d8\'ba\'63Model\'a4\'e2\'aa\'6b: \par
**\'bb\'a1\'a9\'fa: \'a8\'43\'a6\'b8\'b3\'a3\'ad\'ab\'c0\'59\'a7\'e2\'a9\'d2\'a6\'b3\'aa\'baModel\'be\'de\'a7\'40\'a9\'52\'a5\'4f\'b0\'f5\'a6\'e6\'a4\'40\'a6\'b8, \'b3\'6f\'ba\'d8\'a4\'e2\'aa\'6b\'a5\'75\'ad\'6e\'b0\'4f\'bf\'fd\'a9\'d2\'a6\'b3\'aa\'ba\'be\'de\'a7\'40\'ab\'fc\'a5\'4f\'be\'fa\'a5\'76\'ac\'f6\'bf\'fd, \'b4\'4e\'a5\'69\'a5\'48\'c0\'48\'ae\'c9\'ad\'ab\'b2\'7b\'a5\'f4\'a6\'f3\'a4\'40\'ad\'d3\'b6\'a5\'ac\'71\'aa\'baModel\'aa\'ac\'ba\'41, \'a6\'fd\'ac\'4f\'a8\'43\'a6\'b8\'b3\'a3\'ad\'ab\'c0\'59\'ab\'d8\'ba\'63\'a5\'75\'a6\'b3\'be\'41\'a6\'58\'a6\'62\'b8\'ea\'ae\'c6\'b6\'71\'a4\'70\'aa\'ba\'b1\'a1\'aa\'70, \'b0\'f5\'a6\'e6\'b3\'74\'ab\'d7\'a4\'7e\'af\'e0\'c1\'cd\'a9\'f3\'a6\'58\'b2\'7a.\par
**\'c0\'75\'c2\'49: \'be\'41\'a6\'58\'a5\'ce\'a8\'d3\'b0\'6c\'c2\'dc\'be\'e3\'ad\'d3\'ab\'d8\'ba\'63Model\'aa\'ba\'b9\'4c\'b5\'7b,\'a8\'d2\'a6\'70\'b7\'51\'ad\'6e\'b0\'4f\'bf\'fd\'a4\'40\'a6\'ec\'a7\'40\'ae\'61\'aa\'ba\'b3\'d0\'a7\'40\'be\'fa\'b5\'7b,\'a8\'43\'ad\'d3\'a5\'b4\'a6\'72\'aa\'ba\'b7\'73\'bc\'57\'a7\'52\'b0\'a3\'aa\'ac\'ba\'41\'b3\'a3\'ad\'6e\'a7\'b9\'be\'e3\'b0\'4f\'bf\'fd\'ae\'c9; \'a9\'ce\'aa\'cc\'ad\'6e\'ad\'ab\'b2\'7b\'b9\'71\'a4\'6c\'b5\'5e\'bc\'75\'ab\'b5, \'b0\'4f\'bf\'fd\'ad\'b5\'bc\'d6\'ae\'61\'bc\'75\'ab\'b5\'aa\'ba\'a8\'43\'ad\'d3\'c1\'e4\'bd\'4c\'c0\'a3\'a4\'55\'aa\'ba\'ae\'c9\'b6\'a1\'aa\'f8\'b5\'75\'b5\'a5\'ac\'79\'b5\'7b, \'b4\'4e\'b7\'7c\'b1\'c4\'a5\'ce\'b0\'4f\'be\'d0\'a8\'43\'ad\'d3\'be\'de\'a7\'40\'a9\'52\'a5\'4f\'aa\'ba\'a4\'e8\'aa\'6b.\par
**\'af\'ca\'c2\'49:\par
**#\'b8\'ea\'ae\'c6\'b6\'71\'a4\'6a\'ae\'c9,\'ad\'6e\'a7\'e2\'a9\'d2\'a6\'b3\'be\'de\'a7\'40\'ab\'fc\'a5\'4f\'be\'fa\'a5\'76\'b0\'4f\'bf\'fd\'b3\'a3\'b0\'4f\'a6\'ed,\'ab\'44\'b1\'60\'af\'d3\'a5\'ce\'b0\'4f\'be\'d0\'c5\'e9\par
**#\'a8\'43\'a6\'b8Undo\'b3\'a3\'bb\'dd\'ad\'6e\'ad\'ab\'c0\'59\'ab\'d8\'ba\'63, \'a5\'69\'af\'e0\'b7\'7c\'b3\'79\'a6\'a8Undo\'b3\'74\'ab\'d7\'a9\'5f\'ba\'43\'b5\'4c\'a4\'f1\par
**\'af\'ca\'c2\'49\'a7\'ef\'b6\'69 : \'a5\'69\'a5\'48\'b7\'66\'b0\'74\'a8\'e4\'a5\'4c\'a4\'e2\'aa\'6b\'a8\'d3\'bc\'57\'a5\'5bUndo\'b3\'74\'ab\'d7, \'a4\'53\'ab\'4f\'af\'64\'be\'e3\'ad\'d3Model\'ab\'d8\'ba\'63\'b9\'4c\'b5\'7b\par
\par
*\'ac\'4f\'a7\'5f\'a4\'b9\'b3\'5c\'a6\'68\'a6\'b8Undo? \par
**\'bb\'a1\'a9\'fa: \'a4\'b9\'b3\'5c\'a6\'68\'a6\'b8Undo\'c5\'fd\'a5\'ce\'a4\'e1\'a7\'f3\'a6\'b3\'bc\'75\'a9\'ca\'aa\'ba\'be\'de\'a7\'40Book\'a4\'b8\'af\'c0\'aa\'ba\'bc\'57\'a7\'52\'b0\'ca\'a7\'40, \'a6\'d3\'a6\'70\'aa\'47\'ac\'b0\'a4\'46\'c2\'b2\'a4\'c6\'b3\'5d\'ad\'70, \'a4\'5d\'a5\'69\'a5\'48\'a5\'75\'a6\'b3\'b4\'a3\'a8\'d1\'a4\'40\'a6\'b8Undo\'aa\'ba\'af\'e0\'a4\'4f\par
**\'c0\'75\'c2\'49: \'a4\'b9\'b3\'5cUndo\'a6\'68\'a6\'b8\'af\'e0\'c5\'fd\'a5\'ce\'a4\'e1\'a7\'f3\'a6\'b3\'bc\'75\'a9\'ca\'aa\'ba\'be\'de\'a7\'40Book\'bc\'57\'a7\'52\'a9\'52\'a5\'4f\par
**\'af\'ca\'c2\'49:\par
**#\'b3\'5d\'ad\'70\'b7\'7c\'b8\'fb\'bd\'c6\'c2\'f8, \'b5\'7b\'a6\'a1\'bd\'58\'b7\'7c\'b8\'fb\'a6\'68\par
**#\'ad\'59\'b1\'c4\'a5\'ce\'ab\'4f\'a6\'73Model Snapshot\'be\'fa\'a5\'76\'ac\'f6\'bf\'fd\'aa\'ba\'a4\'e2\'aa\'6b\'b9\'ea\'a7\'40, \'b7\'7c\'af\'d3\'a5\'ce\'a7\'f3\'a6\'68\'b0\'4f\'be\'d0\'c5\'e9\par
**#\'ad\'59Model\'b8\'ea\'ae\'c6\'ab\'dc\'a4\'6a\'ab\'dc\'a6\'68\'ae\'c9, \'b1\'c4\'a5\'ce\'ad\'ab\'b7\'73\'ab\'d8\'ba\'63Model\'aa\'ba\'b9\'ea\'a7\'40\'a4\'e2\'aa\'6bUndo\'a6\'68\'a6\'b8\'ae\'c9, \'b7\'7c\'c5\'fd\'b3\'74\'ab\'d7\'ab\'dc\'ba\'43\par
\par
*\'ac\'4f\'a7\'5f\'a4\'b9\'b3\'5cRedo? \par
**\'bb\'a1\'a9\'fa: \'b0\'d3\'a5\'ce\'b2\'a3\'ab\'7e\'a6\'62\'b3\'5d\'ad\'70\'aa\'ba\'ae\'c9\'ad\'d4\'ad\'59\'a6\'b3\'b4\'a3\'a8\'d1Undo\'a5\'5c\'af\'e0, \'a4\'6a\'a6\'68\'b7\'7c\'b7\'66\'b0\'74Redo\'a5\'5c\'af\'e0, \'bd\'54\'ab\'4f\'a6\'62Undo\'a4\'a7\'ab\'e1\'a6\'70\'aa\'47\'a4\'cf\'ae\'ac, \'c1\'d9\'af\'e0\'b0\'f7\'a7\'51\'a5\'ceRedo\'ae\'f8\'b0\'a3Undo\'a9\'d2\'b3\'79\'a6\'a8\'aa\'ba\'ae\'c4\'aa\'47.\par
\par
\'a6\'b3\'c5\'b2\'a9\'f3\'a4\'57\'ad\'7a\'a6\'55\'ba\'d8\'a4\'e2\'aa\'6b\'aa\'ba\'c0\'75\'af\'ca\'c2\'49\'a6\'d2\'bc\'7b, \'a6\'62\'b3\'6f\'ad\'d3Book\'b1\'4d\'ae\'d7\'b7\'ed\'a4\'a4, \'a7\'da\'ad\'cc\'b1\'4e\'b1\'c4\'a5\'ce"\'ab\'4f\'a6\'73Model Snapshot\'be\'fa\'a5\'76\'ac\'f6\'bf\'fd\'a4\'e2\'aa\'6b"\'a8\'d3\'b9\'46\'a6\'a8\'a4\'b9\'b3\'5c\'a6\'68\'a6\'b8Undo\'aa\'ba\'a5\'5c\'af\'e0, \'c1\'f6\'b5\'4d\'ac\'b0\'a4\'46\'c2\'b2\'a4\'c6\'b3\'5d\'ad\'70, \'bc\'c8\'ae\'c9\'a4\'a3\'b9\'ea\'a7\'40Redo, \'a6\'fd\'a4\'b4\'ab\'4f\'af\'64\'a5\'bc\'a8\'d3\'b9\'ea\'a7\'40Redo\'aa\'ba\'bc\'75\'a9\'ca.\par
\par
==\'c3\'fe\'a7\'4f\'ad\'ab\'ba\'63(Refactoring)==\par
\'a9\'7c\'a5\'bc\'a8\'cf\'a5\'ceCommand Pattern\'a4\'a7\'ab\'65, \'b7\'ed\'a6\'62Console\'bf\'e9\'a4\'4a\'ab\'fc\'a5\'4f\'ae\'c9, \'b7\'7c\'aa\'bd\'b1\'b5\'a9\'49\'a5\'73 controller.py \'b7\'ed\'a4\'a4\'aa\'ba add \'a9\'ce delete \'b5\'a5\'a9\'52\'a5\'4f\'a8\'d3\'b7\'73\'bc\'57\'a9\'ce\'a7\'52\'b0\'a3Book\'b7\'ed\'a4\'a4\'aa\'ba\'a4\'b8\'af\'c0, \'a8\'c3\'a5\'42\'aa\'bd\'b1\'b5\'a7\'ef\'c5\'dcModel\'b8\'ea\'ae\'c6\'a4\'ba\'ae\'65. \'ac\'b0\'a4\'46\'ad\'6e\'be\'c9\'a4\'4aCommand Pattern, \'a5\'ce\'a4\'e1\'a6\'62Console\'bf\'e9\'a4\'4a\'ab\'fc\'a5\'4f\'ae\'c9, \'a5\'b2\'b6\'b7\'b1\'4e\'b8\'d3\'ab\'fc\'a5\'4f\'b9\'ef\'c0\'b3\'a8\'ec\'a5\'bf\'bd\'54\'aa\'baConcreteCommand\'c3\'fe\'a7\'4f, \'a8\'c3\'a5\'42\'a9\'49\'a5\'73ConcreteCommand.execute()\'b0\'f5\'a6\'e6\'b9\'ef\'c0\'b3\'aa\'ba\'a9\'52\'a5\'4f. \par
\par
==\'bc\'57\'b2\'4b\'b7\'73\'c3\'fe\'a7\'4f\'a9\'ce\'b7\'73\'a4\'e8\'aa\'6b==\par
*\'b7\'73\'bc\'57 model.py/class ModelSnapshot : \'ac\'b0\'a4\'46\'af\'e0\'b0\'f7\'b0\'4f\'bf\'fd\'a8\'43\'ad\'d3\'b7\'73\'bc\'57\'a7\'52\'b0\'a3\'ae\'c9\'aa\'ba\'be\'fa\'a5\'76\'aa\'ac\'ba\'41, \'a5\'b2\'b6\'b7\'a6\'b3\'a4\'40\'ad\'d3\'b7\'73\'aa\'ba\'c3\'fe\'a7\'4f\'a5\'73\'b0\'b5 ModelSnapshot \'b1\'4d\'aa\'f9\'a5\'ce\'a8\'d3\'ba\'de\'b2\'7a\'b3\'6f\'a8\'c7\'be\'fa\'a5\'76\'ac\'f6\'bf\'fd, \'a6\'70\'aa\'47\'a6\'b3\'bb\'dd\'ad\'6e\'b6\'69\'a6\'e6Undo\'ae\'c9, \'b8\'d3\'c3\'fe\'a7\'4f\'a5\'69\'a5\'48\'a5\'f4\'b7\'4e\'bd\'d5\'a5\'58\'bc\'c6\'ad\'d3\'a8\'42\'c6\'4a\'a4\'a7\'ab\'65\'aa\'baModel\'aa\'ac\'ba\'41, \'c5\'fdBook\'a4\'ba\'ae\'65\'ab\'ec\'b4\'5f\'a8\'ec\'b7\'ed\'ae\'c9\'aa\'ba\'aa\'ac\'ba\'41.\par
*\'b7\'73\'bc\'57 command.py/class Command : \'ac\'4fCommand Pattern\'b7\'ed\'a4\'a4\'a9\'d2\'a6\'b3\'a9\'52\'a5\'4f\'aa\'ba\'a4\'f7\'c3\'fe\'a7\'4f\par
*\'b7\'73\'bc\'57 command.py/class ConcreteCommandAdd : \'a5\'ce\'a8\'d3\'b7\'73\'bc\'57\'ac\'59\'a8\'c7\'a4\'b8\'af\'c0\'a8\'ecBook\'b7\'ed\'a4\'a4\par
*\'b7\'73\'bc\'57 command.py/class ConcreteCommandDelete : \'a5\'ce\'a8\'d3\'a7\'52\'b0\'a3Book\'b7\'ed\'a4\'a4\'aa\'ba\'ac\'59\'a8\'c7\'a4\'b8\'af\'c0\par
*\'b7\'73\'bc\'57 command.py/lstCommandStack: \'b0\'4f\'bf\'fd\'a9\'d2\'a6\'b3command\'aa\'ba\'a9\'49\'a5\'73\'be\'fa\'a5\'76\'aa\'ac\'ba\'41\par
*Console\'b7\'73\'bc\'57undo\'ab\'fc\'a5\'4f : \'a6\'62Console\'ab\'fc\'a5\'4f\'a4\'a4\'a5\'5b\'a4\'4a\'a4\'46undo, \'b7\'ed\'a5\'ce\'a4\'e1\'bf\'e9\'a4\'4a\'b8\'d3\'ab\'fc\'a5\'4f\'ae\'c9\'b7\'7c\'b1\'71lstCommandStack\'b7\'ed\'a4\'a4\'ae\'b3\'a5\'58\'a4\'57\'a6\'b8\'b0\'f5\'a6\'e6\'aa\'baConcreteCommand, \'a8\'c3\'a5\'42\'a9\'49\'a5\'73\'b9\'ef\'c0\'b3\'aa\'baConcreteCommand.undo().\par
\par
==UML\'b9\'cf\'a7\'ce\'bb\'50\'ad\'ab\'ad\'6e\'b5\'7b\'a6\'a1\'bd\'58\'bb\'a1\'a9\'fa==\par
\'b7\'73\'bc\'57\'aa\'bacommand.py\'ad\'74\'b3\'64\'b6\'69\'a6\'e6undo\'aa\'ba\'a5\'f4\'b0\'c8, Package\'aa\'ba\'ac\'db\'a8\'cc\'c3\'f6\'ab\'59\'a6\'70\'a4\'55:\par
  http://lh4.google.com/bohachu/RyVDKgtkWJI/AAAAAAAAApQ/zjdhFm6_gLY/s800/commandPatternPackage.jpg\par
\par
\'b7\'73\'bc\'57\'aa\'bacommand.py\'ad\'ab\'ad\'6e\'c3\'fe\'a7\'4f\'ac\'db\'a8\'cc\'c3\'f6\'ab\'59\'a6\'70\'a4\'55:\par
  http://lh4.google.com/bohachu/RyV9QgtkWLI/AAAAAAAAApk/11ROBX7UC0U/s800/commandPatternClass.jpg\par
\par
\'a5\'48\'a4\'55\'bb\'a1\'a9\'fa\'ad\'ab\'ad\'6e\'c3\'fe\'a7\'4f\'a4\'a7\'b6\'a1\'aa\'ba\'a4\'ac\'b0\'ca\'c3\'f6\'ab\'59\par
*ModelRepository\'ad\'74\'b3\'64\'c0\'78\'a6\'73\'b7\'ed\'ab\'65\'a5\'bf\'a6\'62\'b9\'42\'a7\'40\'aa\'ba\'a9\'d2\'a6\'b3Model\'b8\'ea\'ae\'c6\par
*ModelSnapshot\'ad\'74\'b3\'64\'b1\'4e\'a8\'43\'a6\'b8\'b0\'f5\'a6\'e6Command\'a4\'a7\'ab\'e1\'aa\'baModel\'b8\'ea\'ae\'c6\'b5\'b2\'aa\'47\'c0\'78\'a6\'73\'b0\'5f\'a8\'d3,\'a4\'e8\'ab\'4b\'a4\'e9\'ab\'e1\'b6\'69\'a6\'e6Undo\par
*lstCommandStack\'ad\'74\'b3\'64\'c0\'78\'a6\'73\'a9\'d2\'a6\'b3Command\'aa\'ba\'b0\'f5\'a6\'e6\'be\'fa\'a5\'76\par
*\'b7\'edCommand\'a4\'6c\'c3\'fe\'a7\'4f\'b0\'f5\'a6\'e6Undo\'ae\'c9, \'b7\'7c\'b1\'71lstCommandStack pop\'a5\'58\'b3\'cc\'ab\'e1\'a4\'40\'a6\'b8\'b0\'f5\'a6\'e6\'aa\'ba\'a9\'52\'a5\'4f, \'a6\'41\'b1\'4emodelRepository\'b3\'5d\'a9\'77\'ac\'b0undo\'a4\'a7\'ab\'e1\'aa\'bamodel\'aa\'ac\'ba\'41\par
\par
    #-*- coding: big5 -*-\par
    from log import log\par
    from modelSnapshot import modelSnapshot\par
    from modelRepository import modelRepository\par
    lstCommandStack=[] #\'a9\'d2\'a6\'b3\'be\'de\'a7\'40\'b9\'4c\'aa\'ba command \'be\'fa\'a5\'76\'ac\'f6\'bf\'fd\par
    class Command:\par
        def __init__(self):\par
            lstCommandStack.append(self)\par
            modelSnapshot.addSnapshot(modelRepository.getCloneDic())\par
        def execute(self): pass\par
        def undo(self):\par
            command=lstCommandStack.pop()\par
            log('command.py/Command/undo/%s'%command)\par
            log('command.py/Command/undo/strChildComponentName/%s'%command.strChildComponentName)\par
            modelRepository.setDic(modelSnapshot.getLastCloneDic())\par
    class ConcreteCommandNew(Command):\par
        def __init__(self,strClassName,strComponentName,strText):\par
            Command.__init__(self)\par
            self.strClassName=strClassName\par
            self.strComponentName=strComponentName\par
            self.strText=strText\par
        def execute(self):\par
            modelRepository.new(self.strComponentName,self.strClassName,self.strText)\par
    class ConcreteCommandAdd(Command):\par
        def __init__(self,strParentComponentName,strChildComponentName):\par
            Command.__init__(self)\par
            self.strParentComponentName=strParentComponentName\par
            self.strChildComponentName=strChildComponentName\par
        def execute(self):\par
            modelRepository.add(self.strParentComponentName,self.strChildComponentName)\par
    class ConcreteCommandDelete(Command):\par
        def __init__(self,strComponentName):\par
            self.strComponentName=strComponentName\par
        def execute(self):\par
            modelRepository.delete(self.strComponentName)\par
\par
==Undo \'ad\'ec\'a9\'6c\'b5\'7b\'a6\'a1\'bd\'58==\par
  http://bowenchiudesignpattern.googlecode.com/svn/trunk/undo/\par
\par
=\'b3\'5d\'ad\'70: \'bb\'73\'a7\'40XML I/O\'a5\'5c\'af\'e0=\par
==\'b3\'5d\'ad\'70\'a5\'d8\'bc\'d0\'bb\'50\'b0\'dd\'c3\'44\'b4\'79\'ad\'7a==\par
\'ac\'b0\'a4\'46\'af\'e0\'b1\'4eBook Model\'b8\'ea\'ae\'c6\'a4\'ba\'ae\'65\'bf\'e9\'a5\'58\'a8\'ec\'c0\'c9\'ae\'d7, \'a5\'b2\'b6\'b7\'bc\'57\'b2\'4bXML I/O\'aa\'ba\'b7\'73\'a5\'5c\'af\'e0, \'b0\'a3\'a4\'46\'b4\'a3\'a8\'d1\'b1\'4eModel\'c2\'e0\'b4\'ab\'ac\'b0\'c0\'c9\'ae\'d7\'bf\'e9\'a5\'58\'aa\'ba\'a5\'5c\'af\'e0\'a4\'a7\'a5\'7e, \'a4\'5d\'ad\'6e\'af\'e0\'c5\'aa\'a8\'faXML\'c0\'c9\'ae\'d7\'a4\'ba\'ae\'65, \'a8\'c3\'a5\'42\'b1\'4e\'a4\'a7\'c2\'e0\'b4\'ab\'ac\'b0Book Model\'aa\'ba\'aa\'ed\'a5\'dc\'aa\'6b.\par
\par
==\'b3\'e6\'a4\'b8\'b4\'fa\'b8\'d5(Unit Test)==\par
\'a5\'48\'a4\'55\'ac\'b0XML\'bc\'67\'c0\'c9\'b4\'fa\'b8\'d5, \'a5\'48\'a4\'ce\'c5\'aa\'c0\'c9\'b4\'fa\'b8\'d5, \'a8\'e2\'ba\'d8Test Case\'aa\'ba\'bb\'a1\'a9\'fa:\par
\par
*testSaveXml(): \'b7\'73\'bc\'57\'a4\'40\'a8\'c7\'a4\'b8\'af\'c0\'a8\'ecBook\'a4\'a7\'ab\'e1\'c0\'78\'a6\'73\'ac\'b0XML\'c0\'c9\'ae\'d7\par
#\'bc\'57\'a5\'5b\'a4\'40\'a8\'c7\'a4\'b8\'af\'c0\'a8\'ecBook Model\par
#\'a9\'49\'a5\'73 saveXml()\par
#\'a4\'f1\'b9\'ef\'bf\'e9\'a5\'58\'c0\'c9\'ae\'d7\'aa\'baXML\'a4\'ba\'ae\'65\'b8\'f2\'b9\'77\'b4\'c1\'ac\'4f\'a7\'5f\'ac\'db\'b2\'c5\'a6\'58\par
\par
*testLoadXml(): \'b1\'71\'c0\'c9\'ae\'d7\'c5\'aa\'a8\'faXML\'a8\'c3\'a5\'42\'c5\'e3\'a5\'dc\par
#\'b7\'c7\'b3\'c6\'a6\'6e\'b4\'fa\'b8\'d5\'a5\'ce\'aa\'baXML\'c0\'c9\'ae\'d7\par
#\'a9\'49\'a5\'73 loadXml()\par
#\'a4\'f1\'b9\'ef\'c5\'aa\'a4\'4a\'a4\'a7\'ab\'e1\'a9\'d2\'ab\'d8\'a5\'df\'aa\'baModel\'b8\'ea\'ae\'c6\'ac\'4f\'a7\'5f\'b8\'f2\'b9\'77\'b4\'c1\'aa\'ba\'b2\'c5\'a6\'58\par
\par
==QA\'b4\'fa\'b8\'d5\'b8\'7d\'a5\'bb(QA Testing Script)==\par
QA\'b4\'fa\'b8\'d5\'aa\'ba\'b3\'a1\'a4\'c0\'b1\'4e\'a5\'5d\'a7\'74\'a5\'48\'a4\'55\'b4\'fa\'b8\'d5\'b6\'b5\'a5\'d8, \'a5\'d1QA\'a4\'48\'ad\'fb\'a8\'cf\'a5\'ceConsole\'b9\'ea\'bb\'da\'b6\'69\'a6\'e6\'a4\'e2\'b0\'ca\'be\'de\'a7\'40, \'b4\'fa\'b8\'d5\'a8\'c3\'b0\'4f\'bf\'fd\'b5\'b2\'aa\'47\'ac\'4f\'a7\'5f\'a5\'bf\'bd\'54.\par
\par
*\'bc\'67\'a4\'4aXML\'b4\'fa\'b8\'d5\par
*#\'a4\'e2\'b0\'ca\'a6\'62Console\'b7\'73\'bc\'57\'b3\'5c\'a6\'68Book\'a4\'b8\'af\'c0\par
*#\'a6\'62Console\'a4\'55\'b9\'46writeXml\'ab\'fc\'a5\'4f\'b1\'4eModel\'b1\'c4\'a5\'ceXML\'ae\'e6\'a6\'a1\'bc\'67\'a4\'4a\'c0\'c9\'ae\'d7\par
*#\'a7\'51\'a5\'ce\'a4\'e5\'a6\'72\'bd\'73\'bf\'e8\'be\'b9\'c0\'cb\'ac\'64XML\'c0\'c9\'ae\'d7\'ac\'4f\'a7\'5f\'a4\'ba\'ae\'65\'bb\'50Book Model\'a4\'40\'ad\'50\par
\par
*\'c5\'aa\'a8\'faXML\'b4\'fa\'b8\'d5\par
*#\'a6\'62Console\'a4\'55readXml\'ab\'fc\'a5\'4f\'b1\'71\'c0\'c9\'ae\'d7\'c5\'aa\'a4\'4aXML\par
*#\'a7\'51\'a5\'ceConsole show\'ab\'fc\'a5\'4f\'c5\'e3\'a5\'dcBook\'a4\'ba\'ae\'65\par
*#\'ae\'d6\'b9\'efBook\'c5\'e3\'a5\'dc\'a4\'ba\'ae\'65\'bb\'50XML\'a8\'e2\'aa\'cc\'ac\'4f\'a7\'5f\'a4\'40\'ad\'50\par
\par
==\'a7\'e0\'be\'dc\'b3\'5d\'ad\'70\'a4\'e2\'aa\'6b==\par
XML IO\'a5\'5c\'af\'e0\'a6\'b3\'b4\'58\'ad\'d3\'b9\'ea\'a7\'40\'a4\'e2\'aa\'6b\'a5\'69\'a5\'48\'bf\'ef\'be\'dc:\par
\par
*\'c5\'fdModel\'c3\'fe\'a7\'4f\'a5\'bb\'a8\'ad\'aa\'be\'b9\'44\'a6\'70\'a6\'f3Serialize\'a6\'a8XML\par
**\'bb\'a1\'a9\'fa: \'a8\'43\'ad\'d3Model\'b8\'ea\'ae\'c6\'c3\'fe\'a7\'4f\'b3\'a3\'aa\'be\'b9\'44\'a6\'db\'a4\'76\'aa\'baXML\'aa\'ed\'a5\'dc\'aa\'6b, \'a6\'5d\'a6\'b9\'bf\'e9\'a5\'58XML\'aa\'ba\'ae\'c9\'ad\'d4\'a5\'75\'ad\'6e\'b1\'4eBook Model\'aa\'baTree\'aa\'ac\'b5\'b2\'ba\'63\'b0\'6c\'c2\'dc\'a4\'40\'a6\'b8, \'bd\'d5\'a5\'ce\'a8\'43\'a4\'40\'ba\'d8Book Model\'a4\'6c\'c3\'fe\'a7\'4f\'a5\'bb\'a8\'ad\'aa\'baXML\'aa\'ed\'a5\'dc\'aa\'6b, \'a7\'59\'a5\'69\'b6\'69\'a6\'e6\'bf\'e9\'a5\'58.\par
**\'c0\'75\'c2\'49:\par
**#\'b3\'5d\'ad\'70\'c2\'b2\'b3\'e6\'aa\'bd\'c4\'b1\par
**#\'a5\'d1\'a9\'f3\'a6\'55\'ba\'d8Model\'a4\'6c\'c3\'fe\'a7\'4f\'aa\'baXML\'aa\'ed\'a5\'dc\'aa\'6b\'b3\'a3\'bb\'50Class\'a9\'77\'b8\'71\'a9\'f1\'a6\'62\'a4\'40\'b0\'5f, \'ad\'d7\'a7\'ef\'ae\'c9\'ab\'dc\'ae\'65\'a9\'f6\'a9\'bc\'a6\'b9\'a6\'50\'a8\'42\par
**\'af\'ca\'c2\'49:\par
**#XML\'aa\'ed\'a5\'dc\'aa\'6b\'aa\'ba\'b3\'64\'a5\'f4\'b8\'a8\'a6\'62Model\'c3\'fe\'a7\'4f\'a8\'ad\'a4\'57, \'b1\'4e\'be\'c9\'ad\'50\'a5\'bc\'a8\'d3\'b5\'4c\'aa\'6b\'bc\'75\'a9\'ca\'bf\'e9\'a5\'58\'ac\'b0HTML\'a9\'ce\'a8\'e4\'a5\'4c\'ae\'e6\'a6\'a1\par
**#\'a8\'43\'a6\'b8\'b7\'73\'bc\'57\'a4\'40\'ad\'d3Model\'a4\'6c\'c3\'fe\'a7\'4f, \'b4\'4e\'a5\'b2\'b6\'b7\'b9\'ea\'a7\'40\'b9\'ef\'c0\'b3\'aa\'baXML\'aa\'ed\'a5\'dc\'aa\'6b\'b5\'7b\'a6\'a1\'bd\'58, \'a5\'b2\'b6\'b7\'b1\'4eXML\'bf\'e9\'a5\'58\'bb\'50\'b7\'73\'c3\'fe\'a7\'4f\'a4\'a4\'aa\'ba\'c4\'dd\'a9\'ca\'a4\'40\'a4\'40\'b9\'ef\'c0\'b3, Coding\'a6\'a8\'a5\'bb\'a4\'a3\'a7\'43\par
\par
*\'bb\'73\'a7\'40\'bf\'57\'a5\'df\'aa\'baSerialize\'c3\'fe\'a7\'4f\'c5\'aa\'a8\'faModel\'c2\'e0\'ac\'b0XML\par
**\'bb\'a1\'a9\'fa: \'bf\'57\'a5\'df\'b3\'5d\'ad\'70\'a4\'40\'ad\'d3SerializeXml\'c3\'fe\'a7\'4f, \'b8\'d3\'c3\'fe\'a7\'4f\'a5\'69\'a5\'48\'c5\'aa\'a8\'faModel\'a4\'a7\'ab\'e1, \'b0\'6c\'c2\'dc\'be\'e3\'ad\'d3Book Model\'be\'f0\'aa\'ac\'b5\'b2\'ba\'63, \'bc\'b6\'bc\'67\'c4\'dd\'a9\'ca\'b9\'ef\'c0\'b3\'a8\'ecXML\'aa\'ba\'b5\'7b\'a6\'a1\'bd\'58, \'b1\'4eModel\'aa\'ba\'a8\'43\'ad\'d3\'c4\'dd\'a9\'ca\'a4\'40\'a4\'40\'b9\'ef\'c0\'b3\'a8\'ecXML\'aa\'ed\'a5\'dc\'aa\'6b, \'b5\'4d\'ab\'e1\'bf\'e9\'a5\'58\'ac\'db\'b9\'ef\'c0\'b3\'aa\'baXML\'c0\'c9\'ae\'d7.\par
**\'c0\'75\'c2\'49: Serialize\'c3\'fe\'a7\'4f\'b3\'64\'a5\'f4\'bf\'57\'a5\'df, \'a5\'d1\'a9\'f3Model\'a7\'b9\'a5\'fe\'a4\'a3\'aa\'be\'b9\'44SerializeXml\'aa\'ba\'a6\'73\'a6\'62, IO\'b3\'64\'a5\'f4\'a8\'c3\'a4\'a3\'a6\'62Model\'a8\'ad\'a4\'57, \'a6\'5d\'a6\'b9\'a5\'bc\'a8\'d3\'a5\'75\'ad\'6e\'b7\'73\'bc\'57SerializeHtml\'b4\'4e\'af\'e0\'a7\'ef\'ac\'b0\'b1\'4eModel\'bf\'e9\'a5\'58\'a6\'a8HTML\'c0\'c9\'ae\'d7.\par
**\'af\'ca\'c2\'49: \'a8\'43\'a6\'b8Model\'c3\'fe\'a7\'4f\'aa\'ba\'c4\'dd\'a9\'ca\'ad\'59\'a6\'b3\'a7\'f3\'a7\'ef, \'ab\'68\'a5\'b2\'b6\'b7\'a7\'e4\'a8\'ec\'b9\'ef\'c0\'b3\'aa\'baSerialize\'c3\'fe\'a7\'4f\'b6\'69\'a6\'e6\'ac\'db\'c0\'b3\'ad\'d7\'a7\'ef, \'b5\'7b\'a6\'a1\'bd\'58\'ba\'fb\'c5\'40\'ab\'dc\'c1\'63\'ba\'be, \'a6\'d3\'a5\'42\'a6\'b3\'a5\'69\'af\'e0\'a4\'a3\'a4\'70\'a4\'df\'b5\'6f\'a5\'cd\'a8\'e2\'c3\'e4\'a4\'a3\'a4\'40\'ad\'50\'aa\'ba\'aa\'ac\'aa\'70.\par
\par
*\'a8\'cf\'a5\'ceReflection\'be\'f7\'a8\'ee\'b0\'ca\'ba\'41\'c5\'aa\'a8\'faModel\'c3\'fe\'a7\'4f\'bb\'50\'c4\'dd\'a9\'ca, \'a5\'fe\'a6\'db\'b0\'ca\'c2\'e0\'ac\'b0XML\par
**\'b9\'42\'a5\'ceReflection\'b9\'ef\'c0\'b3Class\'a6\'57\'ba\'d9\'bb\'50XML Tag\'aa\'ba\'a4\'e2\'aa\'6b\'bb\'73\'a7\'40XML I/O\par
**\'bf\'e9\'a5\'58XML\'bb\'a1\'a9\'fa: \'bf\'57\'a5\'df\'b3\'5d\'ad\'70\'a4\'40\'ad\'d3ReflectionSerializeXml\'c3\'fe\'a7\'4f, \'b8\'d3\'c3\'fe\'a7\'4f\'a5\'69\'a5\'48\'a6\'62\'c5\'aa\'a8\'faModel\'a4\'a7\'ab\'e1, \'b0\'6c\'c2\'dc\'be\'e3\'ad\'d3Book Model\'be\'f0\'aa\'ac\'b5\'b2\'ba\'63, \'a7\'51\'a5\'ceReflection\'be\'f7\'a8\'ee\'c5\'aa\'a8\'faModel\'c3\'fe\'a7\'4f\'a4\'a4\'aa\'ba\'a8\'43\'ad\'d3\'c4\'dd\'a9\'ca, \'a8\'c3\'a5\'fe\'a6\'db\'b0\'ca\'b1\'4e\'c4\'dd\'a9\'ca\'a6\'57\'ba\'d9\'b9\'ef\'c0\'b3\'a8\'ecXML Tag, \'a5\'75\'ad\'6eModel\'c3\'fe\'a7\'4f\'a6\'62\'b9\'ea\'a7\'40\'aa\'ba\'ae\'c9\'b2\'c5\'a6\'58\'a6\'db\'b0\'ca\'c2\'e0\'b4\'ab\'aa\'ba\'b3\'57\'bd\'64, \'c4\'dd\'a9\'ca\'b6\'c8\'a8\'cf\'a5\'ce\'a6\'72\'a6\'ea\'a9\'ce\'bc\'c6\'a6\'72\'b5\'a5\'b0\'f2\'a5\'bb\'ab\'ac\'ba\'41\'b4\'4e\'a6\'6e, \'a7\'4f\'a8\'cf\'a5\'ce\'a4\'d3\'bd\'c6\'c2\'f8\'aa\'ba\'c4\'dd\'a9\'ca\'ab\'ac\'ba\'41(\'a8\'d2\'a6\'70\'a6\'62Model\'a4\'a4\'a5\'5d\'a7\'74\'a6\'db\'a9\'77\'b8\'71\'aa\'ba\'c3\'fe\'a7\'4f), \'ab\'68\'a4\'a3\'ba\'de\'b7\'73\'bc\'57\'a6\'68\'a4\'d6\'b7\'73\'aa\'baModel\'a4\'6c\'c3\'fe\'a7\'4f, \'a9\'ce\'aa\'cc\'a5\'f4\'b7\'4e\'a7\'ef\'b0\'caModel\'b7\'ed\'a4\'a4\'aa\'ba\'c4\'dd\'a9\'ca, \'b3\'a3\'a4\'a3\'bb\'dd\'ad\'6e\'c3\'42\'a5\'7e\'ad\'d7\'a7\'ef\'a5\'f4\'a6\'f3XML\'bf\'e9\'a5\'58\'a4\'4a\'aa\'ba\'b5\'7b\'a6\'a1\'bd\'58.\par
**\'bf\'e9\'a4\'4aXML\'bb\'a1\'a9\'fa: \'a8\'cf\'a5\'ceXML Parser\'c5\'aa\'a8\'faXML\'c0\'c9\'ae\'d7\'aa\'baTag\'a4\'a7\'ab\'e1, \'b1\'4e\'a8\'43\'ad\'d3Tag\'a5\'ce\'a9\'54\'a9\'77\'aa\'ba\'b3\'57\'ab\'68\'c2\'e0\'b4\'ab\'ac\'b0Model\'a4\'6c\'c3\'fe\'a7\'4f\'a6\'57\'ba\'d9, \'a9\'49\'a5\'73\'ac\'db\'c0\'b3Model\'c3\'fe\'a7\'4f\'aa\'baconstructor\'b0\'ca\'ba\'41\'ab\'d8\'a5\'dfinstance\'a8\'c3\'b2\'d5\'a6\'58\'ac\'b0\'ad\'ec\'a8\'d3\'aa\'baModel\'be\'f0\'aa\'ac\'b5\'b2\'ba\'63\'aa\'ed\'a5\'dc\'aa\'6b, \'a4\'ba\'b3\'a1\'aa\'ed\'a5\'dcBook\'ae\'c9\'a4\'b4\'b5\'4d\'ac\'4f\'a6\'b9\'b1\'c4\'a5\'ceComposite Pattern\'aa\'ba\'a4\'e8\'a6\'a1, XML Tag\'bb\'50\'c3\'fe\'a7\'4f\'a6\'57\'ba\'d9\'a4\'a7\'b6\'a1\'aa\'ba\'b9\'ef\'c0\'b3\'ac\'4f\'a5\'fe\'a6\'db\'b0\'ca\'c2\'e0\'b4\'ab, \'a4\'a3\'bb\'dd\'ad\'6e\'c3\'42\'a5\'7e\'aa\'ba\'b9\'ef\'c0\'b3\'aa\'ed\'a9\'ce\'aa\'cc\'b5\'7b\'a6\'a1\'bd\'58, \'b0\'a3\'a6\'b9\'a4\'a7\'a5\'7e, XML Tag\'bb\'50\'c3\'fe\'a7\'4f\'c4\'dd\'a9\'ca\'a4\'a7\'b6\'a1\'aa\'ba\'b9\'ef\'c0\'b3\'a4\'5d\'ac\'4f\'a5\'fe\'a6\'db\'b0\'ca\'c2\'e0\'b4\'ab.\par
**\'c0\'75\'c2\'49: \par
**#\'b1\'4eIO\'b5\'7b\'a6\'a1\'bd\'58\'aa\'ba\'b9\'ea\'a7\'40\'a6\'a8\'a5\'bb\'ad\'b0\'a8\'ec\'b3\'cc\'a7\'43, \'bc\'67\'a4\'40\'a6\'b8\'a4\'a7\'ab\'e1\'a5\'c3\'a4\'5b\'a5\'69\'a5\'ce, \'a4\'a3\'bb\'dd\'ad\'d7\'a7\'ef\par
**#Model\'c3\'fe\'a7\'4f\'a7\'b9\'a5\'fe\'a4\'a3\'ba\'deIO, IO\'b3\'64\'a5\'f4\'a7\'b9\'a5\'fe\'bf\'57\'a5\'df, \'a9\'b9\'ab\'e1\'a5\'69\'a5\'48\'b7\'73\'bc\'57\'bf\'e9\'a5\'58\'ac\'b0HTML\'a9\'ce\'a8\'e4\'a5\'4c\'ae\'e6\'a6\'a1\'aa\'ba\'b5\'7b\'a6\'a1\'bd\'58\par
**#\'a4\'a3\'ba\'de\'b7\'73\'bc\'57\'a6\'68\'a4\'d6Model\'a4\'6c\'c3\'fe\'a7\'4f, \'b3\'a3\'a4\'a3\'bb\'dd\'ad\'6e\'a7\'ef\'b0\'caIO\'aa\'ba\'b5\'7b\'a6\'a1\'bd\'58\par
**#\'a5\'f4\'b7\'4e\'ad\'d7\'a7\'efModel\'c4\'dd\'a9\'ca, \'b3\'a3\'a4\'a3\'bb\'dd\'ad\'6e\'a7\'ef\'b0\'caIO\'aa\'ba\'b5\'7b\'a6\'a1\'bd\'58\par
**\'af\'ca\'c2\'49:\par
**#Model\'c4\'dd\'a9\'ca\'a5\'b2\'b6\'b7\'ac\'4f\'b0\'f2\'a5\'bb\'aa\'ba\'a6\'72\'a6\'ea\'a9\'ce\'bc\'c6\'a6\'72\'b5\'a5\'b0\'f2\'a5\'bb\'ab\'ac\'ba\'41, \'a6\'70\'aa\'47Model\'a5\'5d\'a7\'74\'a6\'db\'a9\'77\'b8\'71\'aa\'ba\'c3\'fe\'a7\'4f, \'b4\'4e\'a5\'b2\'b6\'b7\'af\'53\'a7\'4f\'b3\'42\'b2\'7a\par
\par
\'a6\'b3\'c5\'b2\'a9\'f3\'a4\'57\'ad\'7a\'a6\'55\'ba\'d8\'a4\'e2\'aa\'6b\'aa\'ba\'c0\'75\'af\'ca\'c2\'49\'a6\'d2\'bc\'7b, \'a7\'da\'ad\'cc\'b9\'ea\'a7\'40\'a4\'57\'a8\'cf\'a5\'ceReflection\'be\'f7\'a8\'ee\'b0\'ca\'ba\'41\'c5\'aa\'a8\'faModel\'c3\'fe\'a7\'4f\'bb\'50\'c4\'dd\'a9\'ca, \'a5\'fe\'a6\'db\'b0\'ca\'c2\'e0\'ac\'b0XML\'aa\'ba\'a4\'e2\'aa\'6b.\par
\par
==\'bc\'57\'b2\'4b\'b7\'73\'c3\'fe\'a7\'4f\'a9\'ce\'b7\'73\'a4\'e8\'aa\'6b==\par
*\'b7\'73\'bc\'57serialize.py/class ReflectionSerializeXml : \'ad\'74\'b3\'64\'a8\'cf\'a5\'ceReflection\'be\'f7\'a8\'ee\'b1\'bd\'ba\'cbBook Model\'a4\'a7\'ab\'e1\'bf\'e9\'a5\'58\'ac\'b0XML\'c0\'c9\'ae\'d7\par
*\'b7\'73\'bc\'57serialize.py/class XmlReader: \'ad\'74\'b3\'64\'a5\'ceXML SAX Parser\'c5\'aa\'a4\'4a\'b8\'ea\'ae\'c6\'a4\'a7\'ab\'e1, \'b0\'ca\'ba\'41\'b2\'a3\'a5\'cdInstance\'ab\'d8\'a5\'df\'be\'f0\'aa\'acModel\'b5\'b2\'ba\'63\par
\par
==UML\'b9\'cf\'a7\'ce\'bb\'50\'ad\'ab\'ad\'6e\'b5\'7b\'a6\'a1\'bd\'58\'bb\'a1\'a9\'fa==\par
\'b7\'73\'bc\'57\'aa\'baserialize.py\'ad\'74\'b3\'64\'b6\'69\'a6\'e6XML IO\'aa\'ba\'a5\'f4\'b0\'c8, Package\'aa\'ba\'ac\'db\'a8\'cc\'c3\'f6\'ab\'59\'a6\'70\'a4\'55:\par
  http://lh6.google.com/bohachu/RyVEWAtkWKI/AAAAAAAAApY/uPv5-5BiSdU/s800/xmlioPackage.jpg\par
\par
\'a5\'48\'a4\'55\'bb\'a1\'a9\'fa\'ad\'ab\'ad\'6e\'c3\'fe\'a7\'4f\'a4\'a7\'b6\'a1\'aa\'ba\'a4\'ac\'b0\'ca\'c3\'f6\'ab\'59\par
*ReflectionSerializeXml\'b7\'7c\'b1\'c4\'a5\'ce\'bb\'bc\'b0\'6a\'a4\'e8\'a6\'a1\'b1\'bd\'ba\'cb\'a9\'d2\'a6\'b3Book Model\'b8\'ea\'ae\'c6\'a4\'a7\'ab\'e1, \'a7\'ec\'a5\'58\'a8\'43\'ad\'d3BookComponent\'b9\'ea\'bb\'da\'aa\'baClass\'a6\'57\'ba\'d9, \'ab\'d8\'ba\'63XML\'bf\'e9\'a5\'58\par
*\'a5\'d1\'a9\'f3Class\'a6\'57\'ba\'d9\'bb\'50XML Tag\'a4\'a7\'b6\'a1\'aa\'ba\'b9\'ef\'c0\'b3\'ac\'4f\'a5\'ceReflection\'be\'f7\'a8\'ee\'b0\'ca\'ba\'41\'c2\'e0\'b4\'ab, \'a9\'d2\'a5\'48\'a7\'b9\'a5\'fe\'a4\'a3\'bb\'dd\'ad\'6e\'a4\'40\'ad\'d3Mapping Table\'a8\'d3\'b0\'4f\'bf\'fd\'a9\'bc\'a6\'b9\'aa\'ba\'c2\'e0\'b4\'ab\'c3\'f6\'ab\'59, \'a4\'a3\'a6\'fd\'b8\'60\'ac\'d9\'b5\'7b\'a6\'a1\'bd\'58\'bc\'b6\'bc\'67, \'a6\'d3\'a5\'42\'a5\'bc\'a8\'d3\'aa\'ba\'c2\'58\'a5\'52\'a9\'ca\'a4\'5d\'ab\'dc\'b1\'6a, \'a6\'62Model\'b7\'73\'bc\'57\'a5\'f4\'a6\'f3Class, \'b3\'a3\'a5\'69\'a5\'48\'aa\'bd\'b1\'b5\'bf\'e9\'a5\'58\'a6\'a8\'ac\'b0XML, \'a4\'a3\'bb\'dd\'ad\'6e\'c3\'42\'a5\'7e\'ac\'b0\'b7\'73\'aa\'baClass\'bc\'b6\'bc\'67\'a5\'f4\'a6\'f3XML\'bf\'e9\'a5\'58\'a9\'ce\'bf\'e9\'a4\'4a\'b5\'7b\'a6\'a1\'bd\'58.\par
\par
*\'bf\'e9\'a5\'58XML\'aa\'ba\'b5\'7b\'a6\'a1\'bd\'58\'a4\'f9\'ac\'71\par
    class ReflectionSerialzeXml:\par
        def __init__(self):\par
            self.intIndent=0\par
            self.stringIo=StringIO()\par
            self.lstEndTagStack=[]\par
        def write(self,strContent):\par
            self.stringIo.write('%s\\n'%strContent)\par
            print strContent\par
        def saveXml(self):\par
            book=modelRepository.getModelBook()\par
            self.write('<Book strComponentName="modelBook" strText="%s" strAuthor="%s">'%(book.strText,book.strAuthor))\par
            self.constructBookXml(book.lstBookComponent)\par
            self.write('</Book>')\par
            open('output.xml','wb').write(self.stringIo.getvalue())\par
        def getTag(self,obj):\par
            strTag=re.findall(r'<model\\.(.*?) ',str(obj))[0]\par
            return strTag\par
        def constructBookXml(self,lstBookComponent):\par
            if not lstBookComponent: return\par
            for bookComponent in lstBookComponent:\par
                if bookComponent.isLeaf():\par
                    self.write(' '*self.intIndent+'<%s strComponentName="%s" strText="%s"/>'%(self.getTag(bookComponent),bookComponent.strComponentName,bookComponent.strText))\par
                else:\par
                    if bookComponent.strText:\par
                        self.write(' '*self.intIndent+'<%s strComponentName="%s" strText="%s">'%(self.getTag(bookComponent),bookComponent.strComponentName,bookComponent.strText))\par
                        self.lstEndTagStack.append(self.getTag(bookComponent))\par
                    self.intIndent+=4\par
                    self.constructBookXml(bookComponent.lstBookComponent)\par
                    self.intIndent-=4\par
                    self.write(' '*self.intIndent+'</%s>'%(self.lstEndTagStack.pop()))\par
    reflectionSerialzeXml=ReflectionSerialzeXml()\par
\par
*\'bf\'e9\'a5\'58\'ac\'b0XML\'ae\'e6\'a6\'a1\'aa\'ba\'c0\'c9\'ae\'d7 output.xml\par
    <Book strComponentName="modelBook" strText="How to programming in Python" strAuthor="Bowen Chiu">\par
    <Chapter strComponentName="chapter1" strText="Chapter1">\par
        <Section strComponentName="section11" strText="Section 1.1">\par
            <Paragraph strComponentName="paragraph111" strText="This is paragraph 1 of section 1.1"/>\par
            <Paragraph strComponentName="paragraph112" strText="This is paragraph 2 of section 1.1"/>\par
            <Section strComponentName="section111" strText="Section 1.1.1">\par
                <Paragraph strComponentName="paragraph1111" strText="This is paragraph 1 of section 1.1.1"/>\par
                <UnorderedList strComponentName="unorderedList111" strText="Following is UnorderedList">\par
                    <BulletItem strComponentName="bulletItem1111" strText=". bulletItem1"/>\par
                    <BulletItem strComponentName="bulletItem1112" strText=". bulletItem2"/>\par
                    <BulletItem strComponentName="bulletItem1113" strText=". bulletItem3"/>\par
                </UnorderedList>\par
            </Section>\par
            <Section strComponentName="section112" strText="Section 1.1.2">\par
            </Section>\par
        </Section>\par
    </Chapter>\par
    <Chapter strComponentName="chapter2" strText="Chapter2">\par
        <Section strComponentName="section21" strText="Section 2.1">\par
            <Paragraph strComponentName="paragraph21" strText="This is paragraph 1 of section 2.1"/>\par
        </Section>\par
    </Chapter>\par
    <Chapter strComponentName="c3" strText="Chapter3">\par
        <Paragraph strComponentName="p3" strText="This is new paragraph 1 of Chapter 3"/>\par
    </Chapter>\par
    </Book>\par
\par
*\'b1\'71XML\'c0\'c9\'ae\'d7\'c5\'aa\'a4\'4a\'b8\'ea\'ae\'c6\'a4\'a7\'ab\'e1,\'b8\'67\'b9\'4cSAX Parser\'b0\'ca\'ba\'41\'ab\'d8\'a5\'dfClass Instance\'a8\'c3\'a5\'42\'bd\'e1\'a4\'a9\'ad\'c8, \'bd\'d0\'aa\'60\'b7\'4e, \'a4\'e9\'ab\'e1\'a6\'70\'aa\'47\'bc\'57\'a5\'5b\'b7\'73\'aa\'baModel Class, \'a4\'5d\'a7\'b9\'a5\'fe\'a4\'a3\'bb\'dd\'ad\'6e\'bc\'b6\'bc\'67\'c3\'42\'a5\'7e\'aa\'ba\'b9\'ef\'c0\'b3\'aa\'ed\'a8\'d3\'b2\'a3\'a5\'cdInstance, \'a6\'5d\'ac\'b0XML Tag\'bb\'50Class\'a6\'57\'ba\'d9\'a4\'a7\'b6\'a1\'aa\'ba\'b9\'ef\'c0\'b3\'c3\'f6\'ab\'59\'ac\'4f\'b1\'c4\'a5\'ceReflection\'a4\'e2\'aa\'6b\'c2\'e0\'b4\'ab\'aa\'ba, \'a9\'d2\'a5\'48Model Class\'a5\'69\'a5\'48\'bc\'75\'a9\'ca\'aa\'ba\'a4\'a3\'c2\'5f\'bc\'57\'a5\'5b, \'a6\'d3\'a7\'b9\'a5\'fe\'a4\'a3\'b7\'7c\'bc\'76\'c5\'54\'a8\'ecXML IO\'aa\'ba\'b5\'7b\'a6\'a1\'bd\'58.\par
\par
    import xml.sax.handler\par
    class XmlReader(xml.sax.handler.ContentHandler):\par
        def __init__(self):\par
            self.lstBookComponent=[]\par
            modelRepository.clear()\par
        def startElement(self,name,attributes):\par
            strClass=str(name)\par
            strComponentName=str(attributes['strComponentName'])\par
            strText=str(attributes['strText'])\par
            bookComponent=modelRepository.new(strComponentName,strClass,strText)\par
            if self.lstBookComponent:\par
                modelRepository.add(self.lstBookComponent[-1].strComponentName,strComponentName)\par
            if bookComponent.isLeaf()==False:\par
                self.lstBookComponent.append(bookComponent)\par
        def characters(self, data): pass\par
        def endElement(self, name):\par
            bookComponent=modelRepository.new('testTypeOnly',name,'')\par
            if bookComponent.isLeaf()==False:\par
                self.lstBookComponent.pop()\par
\par
*\'b1\'71\'c0\'c9\'ae\'d7\'c5\'aa\'a8\'faXML\'b8\'ea\'ae\'c6\'a4\'a7\'ab\'e1,\'a5\'ce\'be\'f0\'aa\'ac\'b5\'b2\'ba\'63\'c5\'e3\'a5\'dcModel\'aa\'ba\'b5\'b2\'aa\'47\par
    Book Title:How to programming in Python\par
    Chapter1\par
        Section 1.1\par
            This is paragraph 1 of section 1.1\par
            This is paragraph 2 of section 1.1\par
            Section 1.1.1\par
                This is paragraph 1 of section 1.1.1\par
                Following is UnorderedList\par
                    . bulletItem1\par
                    . bulletItem2\par
                    . bulletItem3\par
            Section 1.1.2\par
    Chapter2\par
        Section 2.1\par
            This is paragraph 1 of section 2.1\par
    Chapter3\par
        This is new paragraph 1 of Chapter 3\par
\par
==XML IO\'ad\'ec\'a9\'6c\'b5\'7b\'a6\'a1\'bd\'58==\par
  http://bowenchiudesignpattern.googlecode.com/svn/trunk/xmlio/\par
\par
}
 